#include "mcu_com.h"

////////////////////////////////////////////////////////////////////////////
//						MCU 调试框架
////////////////////////////////////////////////////////////////////////////
//1、IAP
//2、内存访问
//3、UID
int void_fun_int(MCU_SYS_CMD c){return 0;} //空函数
void void_fun_pn(u8 *p,int n){} //空函数
void void_fun_opn(int off,u8 *p,int n){} //空函数
///////////////////////////////////////////////////////////////////////
//				IAP外部接口
int (*iap_cmd_fun)(MCU_SYS_CMD c)=void_fun_int; //iap调用主程序的指令
void (*iap_tx)(u8 *p,int n)=void_fun_pn; //IAP数据发送函数
//IAP数据存储，主程序可以缓存在内存，也可以直接烧flash
void (*iap_store)(int off,u8 *p,int n)=void_fun_opn; //输入存储偏置，数据指针，长度

const u8 *binbuf_p=0; //程序缓存位置，但不能写只能读。写是通过函数来写
int binbuf_n=0; //程序缓存长度
u8 *iap_txbuf=0; //发送缓冲区，至少sizeof(COM_DBG)那么长。若有块数据外发，则更大
int iap_txbuf_len=0; //发送缓冲区长度
////////////////////////////////////////////////////////////////////////////
//				2、内存访问
u16 mem_off_tab[12]= //内存区域表,基址的高16bit
{
	0, //0:相对地址(0x2000后的低16bit偏移)
	0x0800,0x0801,//1/2:flash区,可根据具体mcu修改
	0x1000, //3:CCM data
	0x2000,0x2001, //4/5:ram
	0x4000,0x4001,0x4002, //6/7/8:外设区
	0x1fff, //9:选项字
	0,//10:版本号(0x2000后的低16bit偏移)
	0,//11:UID(0x2000后的低16bit偏移)
}; //为0的部分需要具体MCU实现赋值
#define CD (*(COM_DBG*)p)
void pro_dbg(u8 *p) //处理调试帧，输入首指针，原位操作
{
	u32 pd=mem_off_tab[CD.para]<<16; //内存基址，为0的后边会覆盖
	if(CD.para>ARRAY_SIZE(mem_off_tab) || CD.cmd>3 || CD.type>2) return;
	if(CD.para==0 || CD.para==11) //若是相对地址
	{
		pd=(0x2000<<16) + mem_off_tab[CD.para];
	}
	else if(CD.para==10) //若是版本号
	{
		pd=(0x0800<<16) + mem_off_tab[CD.para];
	}
	pd=pd + CD.d16; //实际地址为：基址 + 偏移
	switch(CD.cmd)
	{
	case 0: //读
		CD.d32 = *(u32*)pd;
		CD.cmd=3;
		break;
	case 1: //写
		if(CD.type==0) *(u8*)pd=CD.d32;
		else if(CD.type==1) *(u16*)pd=CD.d32;
		else *(u32*)pd=CD.d32;
		break;
	case 2: //保存
		iap_cmd_fun(MCU_SAVE);
	default:
		return;
	}
}
///////////////////////////////////////////////////////////////////////
//				IAP变量
///////////////////////////////////////////////////////////////////////
#define txbuf (*(COM_DBG*)iap_txbuf) //发送缓存
u32 first_tick=1*100; //等1s
u32 iap_req_tick=0; //请求发送的延迟时钟
u32 targ_len=0; //目标长度,单位B
u16 targ_crc=0; //目标crc
u32 binbuf_ind=0; //下载进度
u32 iap_stat=0; //0待机，1开始接收数据
void iap_req_data(void) //请求数据
{
	iap_txbuf[0]=0; //清发送缓存
	if(txbuf.para==15) txbuf.para=0; //包序号归零
	txbuf.cmd=IAP_U_GETD; //6请求数据：d32为所需的数据偏移
	txbuf.d16=0;
	txbuf.para++;//包序号 1-15
	txbuf.d32=binbuf_ind;  //请求数据的偏移
	iap_tx((u8*)&txbuf,sizeof(COM_DBG));
}
void iap_poll(void) //100Hz
{
	if(iap_stat==1) //若正在接收数据
	{
		if(iap_req_tick) iap_req_tick--;
		else
		{
			iap_req_tick=30; //超时300ms
			iap_req_data(); //发送请求
		}
	}
	if(first_tick) //等1s，若正在接收，则等的时间延长
	{
		first_tick--;
		return ;
	}
	//若超时了
	if(iap_stat==0) iap_cmd_fun(MCU_SWITCH_APP); //超时了，若还待机，切换到主程序
	//超时了，还在接收数据，重启
	iap_cmd_fun(MCU_RESET);
}
void iap_pro_cmd_4(u8 *p) //处理4号指令IAP_D_REQ
{ //para=0请求帧,para=1查询帧
	iap_txbuf[0]=0; //清发送缓存
	txbuf.para=CD.para; //回复帧的para为请求类型
	txbuf.cmd=IAP_U_ANS; //5应答查询：d32为版本
	switch(CD.para) //判断查询的类型
	{
	case 0: //请求帧,data为bin的crc16,d32为下载总长B
		if(CD.d32<=binbuf_n) //若能执行
		{
			targ_crc=CD.d16;
			targ_len=CD.d32; //记录bin信息
			iap_stat=1; //进入接收数据状态
			binbuf_ind=0; //初始化数据状态
			txbuf.d16=0; //0准许
		}
		else txbuf.d16=1; //空间不足 1不准许
		txbuf.d32=0;
		iap_tx(iap_txbuf,sizeof(COM_DBG));
		printf("req: %s\r\n",txbuf.para==0?"ok":"err");
		break;
	case 1: //查询帧
		txbuf.d16=0;
		txbuf.d32=uid; //版本
		iap_tx(iap_txbuf,sizeof(COM_DBG));
		break;
	case 2: iap_cmd_fun(MCU_RESET); break; //2重启
	}
}
void iap_rx(u8 *p,int n) //IAP的接收函数
{
	first_tick=10*100; //10s超时时间
	if(CD.down==0) //若不是下行数据看是什么指令
	{
		if(CD.cmd<=DBG_U_REP) pro_dbg(p); //若是debug指令
		switch(CD.cmd)
		{
		case IAP_D_REQ: iap_pro_cmd_4(p); break; //4请求指令
		}
	}
	else //若是下行数据
	{
		int r=0; //结果
		int len=p[0] & 0x7f; //数据长度，单位B
		if(CD.para == txbuf.para) //包序号相等才存，下发数据里没有索引位置，会存重
		{
			iap_store(binbuf_ind, CD.d, len); //存下来
			binbuf_ind+=len;
		}
		else return ; //包序号不等，抛弃
		if(binbuf_ind>=targ_len) //若已经收到了所有数据
		{
			//校验
			u16 crc=crc_ccitt((u8*)binbuf_p,targ_len);
 			if(crc!=targ_crc) r=3; // //若crc不同，3crc错误
			else //正确
			{
				r=iap_cmd_fun(MCU_IAP_FINISH); //完成IAP，调用烧写,出错后返回1
				if(r) r=4; //4烧写错误
				first_tick=1*100; //1s超时时间，等包发出就重启了
			}
			if(r) printf("flash err\r\n"); //crc错，或flash烧写错误
			else r=5; //5烧写成功
			iap_stat=0;
			targ_len=0;
			binbuf_ind=0; //初始化数据状态

			iap_txbuf[0]=0; //清发送缓存
			txbuf.cmd=IAP_U_ANS; //5应答请求
			txbuf.para=0; //
			txbuf.d16=r;
			txbuf.d32=0;
			iap_tx(iap_txbuf,sizeof(COM_DBG));
		}
		else //还有数据，继续请求
		{
			iap_req_data();
			iap_req_tick=30; //超时300ms
		}
	}
}
////////////////////////////////////////////////////////////////////////////
//3、UID
u32 uid=0; //唯一ID

